/*
 * Copyright 2012-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.autoconfigure.flyway;

import java.io.File;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
import org.springframework.boot.convert.DurationUnit;

/**
 * Flyway 数据库迁移的配置属性。
 *
 * @author Dave Syer
 * @author Eddú Meléndez
 * @author Stephane Nicoll
 * @author Chris Bono
 * @since 1.1.0
 */
@ConfigurationProperties(prefix = "spring.flyway")
public class FlywayProperties {

	/**
	 * 是否启用 Flyway。
	 */
	private boolean enabled = true;

	/**
	 * 如果迁移脚本的位置不存在，是否失败。
	 */
	private boolean failOnMissingLocations;

	/**
	 * 迁移脚本的位置。可以包含特殊的 "{vendor}" 占位符以使用特定数据库厂商的位置。
	 */
	private List<String> locations = new ArrayList<>(Collections.singletonList("classpath:db/migration"));

	/**
	 * SQL 迁移文件的编码。
	 */
	private Charset encoding = StandardCharsets.UTF_8;

	/**
	 * 连接数据库时最大重试次数。
	 */
	private int connectRetries;

	/**
	 * 连接数据库时重试的最大间隔时间。如果未指定时间单位，默认使用秒。
	 */
	@DurationUnit(ChronoUnit.SECONDS)
	private Duration connectRetriesInterval;

	/**
	 * 获取锁时最大重试次数。
	 */
	private Integer lockRetryCount;

	/**
	 * Flyway 管理的默认数据库模式名称（区分大小写）。
	 */
	private String defaultSchema;

	/**
	 * Flyway 管理的数据库模式名称列表（区分大小写）。
	 */
	private List<String> schemas = new ArrayList<>();

	/**
	 * Flyway 是否应尝试创建 schemas 属性中指定的数据库模式。
	 */
	private boolean createSchemas = true;

	/**
	 * Flyway 使用的 schema 版本历史表名称。
	 */
	private String table = "flyway_schema_history";

	/**
	 * schema 版本历史表所在的表空间。若数据库不支持表空间则忽略。默认为连接的默认表空间。
	 */
	private String tablespace;

	/**
	 * 基线标记时使用的描述信息。
	 */
	private String baselineDescription = "<< Flyway Baseline >>";

	/**
	 * 执行基线时使用的版本号。
	 */
	private String baselineVersion = "1";

	/**
	 * 在 schema 版本历史表中记录执行迁移的用户名。
	 */
	private String installedBy;

	/**
	 * 用于 SQL 脚本中的占位符及其替换值。
	 */
	private Map<String, String> placeholders = new HashMap<>();

	/**
	 * SQL 脚本中占位符的前缀。
	 */
	private String placeholderPrefix = "${";

	/**
	 * SQL 脚本中占位符的后缀。
	 */
	private String placeholderSuffix = "}";

	/**
	 * 默认占位符的分隔符。
	 */
	private String placeholderSeparator;

	/**
	 * 是否在迁移脚本中执行占位符替换。
	 */
	private boolean placeholderReplacement = true;

	/**
	 * SQL 迁移文件的前缀。
	 */
	private String sqlMigrationPrefix = "V";

	/**
	 * SQL 迁移文件的后缀列表。
	 */
	private List<String> sqlMigrationSuffixes = new ArrayList<>(Collections.singleton(".sql"));

	/**
	 * SQL 迁移文件的分隔符。
	 */
	private String sqlMigrationSeparator = "__";

	/**
	 * 可重复执行的 SQL 迁移文件的前缀。
	 */
	private String repeatableSqlMigrationPrefix = "R";

	/**
	 * 迁移的目标版本。
	 */
	private String target;

	/**
	 * 数据库用户名。
	 */
	private String user;

	/**
	 * 数据库密码。
	 */
	private String password;

	/**
	 * JDBC 驱动类名。默认根据 URL 自动检测。
	 */
	private String driverClassName;

	/**
	 * 数据库连接 URL。如果未设置，则使用主数据源。
	 */
	private String url;

	/**
	 * 获取连接后立即执行的初始化 SQL 语句列表。
	 */
	private List<String> initSqls = new ArrayList<>();

	/**
	 * 迁移非空 schema 时是否自动执行基线操作。
	 */
	private boolean baselineOnMigrate;

	/**
	 * 是否禁用清理操作。
	 */
	private boolean cleanDisabled;

	/**
	 * 当验证失败时，是否自动执行清理。
	 */
	private boolean cleanOnValidationError;

	/**
	 * 应用迁移时是否将所有待迁移脚本合并到同一个事务中。
	 */
	private boolean group;

	/**
	 * 是否忽略读取 schema 版本历史表时缺失的迁移。
	 * @deprecated 已弃用
	 */
	@Deprecated
	private boolean ignoreMissingMigrations;

	/**
	 * 是否忽略读取 schema 版本历史表时被忽略的迁移。
	 * @deprecated 已弃用
	 */
	@Deprecated
	private boolean ignoreIgnoredMigrations;

	/**
	 * 是否忽略读取 schema 版本历史表时待执行的迁移。
	 * @deprecated 已弃用
	 */
	@Deprecated
	private boolean ignorePendingMigrations;

	/**
	 * 是否忽略读取 schema 版本历史表时未来的迁移。
	 * @deprecated 已弃用，默认为 true
	 */
	@Deprecated
	private boolean ignoreFutureMigrations = true;

	/**
	 * 是否允许在同一迁移中混合事务性和非事务性语句。
	 */
	private boolean mixed;

	/**
	 * 是否允许迁移按顺序外执行。
	 */
	private boolean outOfOrder;

	/**
	 * 是否跳过默认回调。为 true 时仅使用自定义回调。
	 */
	private boolean skipDefaultCallbacks;

	/**
	 * 是否跳过默认解析器。为 true 时仅使用自定义解析器。
	 */
	private boolean skipDefaultResolvers;

	/**
	 * 是否验证迁移和回调脚本的命名规范。
	 */
	private boolean validateMigrationNaming = false;

	/**
	 * 迁移时是否自动执行验证。
	 */
	private boolean validateOnMigrate = true;

	/**
	 * 是否批量执行 SQL 语句。需要 Flyway Teams 版支持。
	 */
	private Boolean batch;

	/**
	 * 迁移 dry run 时输出的 SQL 文件。需要 Flyway Teams 版支持。
	 */
	private File dryRunOutput;

	/**
	 * 内置错误处理覆盖规则。需要 Flyway Teams 版支持。
	 */
	private String[] errorOverrides;

	/**
	 * Flyway Teams 版许可证密钥。
	 */
	private String licenseKey;

	/**
	 * 是否启用 Oracle SQL*Plus 命令支持。需要 Flyway Teams 版支持。
	 */
	private Boolean oracleSqlplus;

	/**
	 * 遇到不支持的 Oracle SQL*Plus 语句时是否发出警告而非错误。需要 Flyway Teams 版支持。
	 */
	private Boolean oracleSqlplusWarn;

	/**
	 * 执行迁移时是否启用流式执行。需要 Flyway Teams 版支持。
	 */
	private Boolean stream;

	/**
	 * Undo SQL 迁移文件的前缀。需要 Flyway Teams 版支持。
	 */
	private String undoSqlMigrationPrefix;

	/**
	 * Flyway 迁移时考虑的迁移文件列表。为空时考虑全部。需要 Flyway Teams 版支持。
	 */
	private String[] cherryPick;

	/**
	 * 传递给 JDBC 驱动的属性。需要 Flyway Teams 版支持。
	 */
	private Map<String, String> jdbcProperties = new HashMap<>();

	/**
	 * Kerberos 配置文件路径。需要 Flyway Teams 版支持。
	 */
	private String kerberosConfigFile;

	/**
	 * Oracle Kerberos 缓存文件路径。需要 Flyway Teams 版支持。
	 */
	private String oracleKerberosCacheFile;

	/**
	 * Oracle 钱包位置，用于自动登录数据库。需要 Flyway Teams 版支持。
	 */
	private String oracleWalletLocation;

	/**
	 * 是否在执行迁移时输出查询结果表。需要 Flyway Teams 版支持。
	 */
	private Boolean outputQueryResults;

	/**
	 * SQL Server Kerberos 登录文件路径。需要 Flyway Teams 版支持。
	 */
	private String sqlServerKerberosLoginFile;

	/**
	 * 是否跳过执行迁移内容，仅更新版本历史表。需要 Flyway Teams 版支持。
	 */
	private Boolean skipExecutingMigrations;

	/**
	 * 验证迁移时忽略的迁移模式列表。需要 Flyway Teams 版支持。
	 */
	private List<String> ignoreMigrationPatterns;

	/**
	 * 是否尝试自动检测 SQL 迁移文件的编码。需要 Flyway Teams 版支持。
	 */
	private Boolean detectEncoding;

	/**
	 * 基线迁移文件的文件名前缀。需要 Flyway Teams 版支持。
	 */
	private String baselineMigrationPrefix;

	/**
	 * 迁移脚本中占位符的前缀。
	 */
	private String scriptPlaceholderPrefix;

	/**
	 * 迁移脚本中占位符的后缀。
	 */
	private String scriptPlaceholderSuffix;

	public boolean isEnabled() {
		return this.enabled;
	}

	public void setEnabled(boolean enabled) {
		this.enabled = enabled;
	}

	public boolean isFailOnMissingLocations() {
		return this.failOnMissingLocations;
	}

	public void setFailOnMissingLocations(boolean failOnMissingLocations) {
		this.failOnMissingLocations = failOnMissingLocations;
	}

	public List<String> getLocations() {
		return this.locations;
	}

	public void setLocations(List<String> locations) {
		this.locations = locations;
	}

	public Charset getEncoding() {
		return this.encoding;
	}

	public void setEncoding(Charset encoding) {
		this.encoding = encoding;
	}

	public int getConnectRetries() {
		return this.connectRetries;
	}

	public void setConnectRetries(int connectRetries) {
		this.connectRetries = connectRetries;
	}

	public Duration getConnectRetriesInterval() {
		return this.connectRetriesInterval;
	}

	public void setConnectRetriesInterval(Duration connectRetriesInterval) {
		this.connectRetriesInterval = connectRetriesInterval;
	}

	public Integer getLockRetryCount() {
		return this.lockRetryCount;
	}

	public void setLockRetryCount(Integer lockRetryCount) {
		this.lockRetryCount = lockRetryCount;
	}

	public String getDefaultSchema() {
		return this.defaultSchema;
	}

	public void setDefaultSchema(String defaultSchema) {
		this.defaultSchema = defaultSchema;
	}

	public List<String> getSchemas() {
		return this.schemas;
	}

	public void setSchemas(List<String> schemas) {
		this.schemas = schemas;
	}

	public boolean isCreateSchemas() {
		return this.createSchemas;
	}

	public void setCreateSchemas(boolean createSchemas) {
		this.createSchemas = createSchemas;
	}

	public String getTable() {
		return this.table;
	}

	public void setTable(String table) {
		this.table = table;
	}

	public String getTablespace() {
		return this.tablespace;
	}

	public void setTablespace(String tablespace) {
		this.tablespace = tablespace;
	}

	public String getBaselineDescription() {
		return this.baselineDescription;
	}

	public void setBaselineDescription(String baselineDescription) {
		this.baselineDescription = baselineDescription;
	}

	public String getBaselineVersion() {
		return this.baselineVersion;
	}

	public void setBaselineVersion(String baselineVersion) {
		this.baselineVersion = baselineVersion;
	}

	public String getInstalledBy() {
		return this.installedBy;
	}

	public void setInstalledBy(String installedBy) {
		this.installedBy = installedBy;
	}

	public Map<String, String> getPlaceholders() {
		return this.placeholders;
	}

	public void setPlaceholders(Map<String, String> placeholders) {
		this.placeholders = placeholders;
	}

	public String getPlaceholderPrefix() {
		return this.placeholderPrefix;
	}

	public void setPlaceholderPrefix(String placeholderPrefix) {
		this.placeholderPrefix = placeholderPrefix;
	}

	public String getPlaceholderSuffix() {
		return this.placeholderSuffix;
	}

	public void setPlaceholderSuffix(String placeholderSuffix) {
		this.placeholderSuffix = placeholderSuffix;
	}

	public String getPlaceholderSeparator() {
		return this.placeholderSeparator;
	}

	public void setPlaceholderSeparator(String placeholderSeparator) {
		this.placeholderSeparator = placeholderSeparator;
	}

	public boolean isPlaceholderReplacement() {
		return this.placeholderReplacement;
	}

	public void setPlaceholderReplacement(boolean placeholderReplacement) {
		this.placeholderReplacement = placeholderReplacement;
	}

	public String getSqlMigrationPrefix() {
		return this.sqlMigrationPrefix;
	}

	public void setSqlMigrationPrefix(String sqlMigrationPrefix) {
		this.sqlMigrationPrefix = sqlMigrationPrefix;
	}

	public List<String> getSqlMigrationSuffixes() {
		return this.sqlMigrationSuffixes;
	}

	public void setSqlMigrationSuffixes(List<String> sqlMigrationSuffixes) {
		this.sqlMigrationSuffixes = sqlMigrationSuffixes;
	}

	public String getSqlMigrationSeparator() {
		return this.sqlMigrationSeparator;
	}

	public void setSqlMigrationSeparator(String sqlMigrationSeparator) {
		this.sqlMigrationSeparator = sqlMigrationSeparator;
	}

	public String getRepeatableSqlMigrationPrefix() {
		return this.repeatableSqlMigrationPrefix;
	}

	public void setRepeatableSqlMigrationPrefix(String repeatableSqlMigrationPrefix) {
		this.repeatableSqlMigrationPrefix = repeatableSqlMigrationPrefix;
	}

	public String getTarget() {
		return this.target;
	}

	public void setTarget(String target) {
		this.target = target;
	}

	public String getUser() {
		return this.user;
	}

	public void setUser(String user) {
		this.user = user;
	}

	public String getPassword() {
		return (this.password != null) ? this.password : "";
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getDriverClassName() {
		return this.driverClassName;
	}

	public void setDriverClassName(String driverClassName) {
		this.driverClassName = driverClassName;
	}

	public String getUrl() {
		return this.url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public List<String> getInitSqls() {
		return this.initSqls;
	}

	public void setInitSqls(List<String> initSqls) {
		this.initSqls = initSqls;
	}

	public boolean isBaselineOnMigrate() {
		return this.baselineOnMigrate;
	}

	public void setBaselineOnMigrate(boolean baselineOnMigrate) {
		this.baselineOnMigrate = baselineOnMigrate;
	}

	public boolean isCleanDisabled() {
		return this.cleanDisabled;
	}

	public void setCleanDisabled(boolean cleanDisabled) {
		this.cleanDisabled = cleanDisabled;
	}

	public boolean isCleanOnValidationError() {
		return this.cleanOnValidationError;
	}

	public void setCleanOnValidationError(boolean cleanOnValidationError) {
		this.cleanOnValidationError = cleanOnValidationError;
	}

	public boolean isGroup() {
		return this.group;
	}

	public void setGroup(boolean group) {
		this.group = group;
	}

	@Deprecated
	@DeprecatedConfigurationProperty(replacement = "spring.flyway.ignore-migration-patterns")
	public boolean isIgnoreMissingMigrations() {
		return this.ignoreMissingMigrations;
	}

	@Deprecated
	public void setIgnoreMissingMigrations(boolean ignoreMissingMigrations) {
		this.ignoreMissingMigrations = ignoreMissingMigrations;
	}

	@Deprecated
	@DeprecatedConfigurationProperty(replacement = "spring.flyway.ignore-migration-patterns")
	public boolean isIgnoreIgnoredMigrations() {
		return this.ignoreIgnoredMigrations;
	}

	@Deprecated
	public void setIgnoreIgnoredMigrations(boolean ignoreIgnoredMigrations) {
		this.ignoreIgnoredMigrations = ignoreIgnoredMigrations;
	}

	@Deprecated
	@DeprecatedConfigurationProperty(replacement = "spring.flyway.ignore-migration-patterns")
	public boolean isIgnorePendingMigrations() {
		return this.ignorePendingMigrations;
	}

	@Deprecated
	public void setIgnorePendingMigrations(boolean ignorePendingMigrations) {
		this.ignorePendingMigrations = ignorePendingMigrations;
	}

	@Deprecated
	@DeprecatedConfigurationProperty(replacement = "spring.flyway.ignore-migration-patterns")
	public boolean isIgnoreFutureMigrations() {
		return this.ignoreFutureMigrations;
	}

	@Deprecated
	public void setIgnoreFutureMigrations(boolean ignoreFutureMigrations) {
		this.ignoreFutureMigrations = ignoreFutureMigrations;
	}

	public boolean isMixed() {
		return this.mixed;
	}

	public void setMixed(boolean mixed) {
		this.mixed = mixed;
	}

	public boolean isOutOfOrder() {
		return this.outOfOrder;
	}

	public void setOutOfOrder(boolean outOfOrder) {
		this.outOfOrder = outOfOrder;
	}

	public boolean isSkipDefaultCallbacks() {
		return this.skipDefaultCallbacks;
	}

	public void setSkipDefaultCallbacks(boolean skipDefaultCallbacks) {
		this.skipDefaultCallbacks = skipDefaultCallbacks;
	}

	public boolean isSkipDefaultResolvers() {
		return this.skipDefaultResolvers;
	}

	public void setSkipDefaultResolvers(boolean skipDefaultResolvers) {
		this.skipDefaultResolvers = skipDefaultResolvers;
	}

	public boolean isValidateMigrationNaming() {
		return this.validateMigrationNaming;
	}

	public void setValidateMigrationNaming(boolean validateMigrationNaming) {
		this.validateMigrationNaming = validateMigrationNaming;
	}

	public boolean isValidateOnMigrate() {
		return this.validateOnMigrate;
	}

	public void setValidateOnMigrate(boolean validateOnMigrate) {
		this.validateOnMigrate = validateOnMigrate;
	}

	public Boolean getBatch() {
		return this.batch;
	}

	public void setBatch(Boolean batch) {
		this.batch = batch;
	}

	public File getDryRunOutput() {
		return this.dryRunOutput;
	}

	public void setDryRunOutput(File dryRunOutput) {
		this.dryRunOutput = dryRunOutput;
	}

	public String[] getErrorOverrides() {
		return this.errorOverrides;
	}

	public void setErrorOverrides(String[] errorOverrides) {
		this.errorOverrides = errorOverrides;
	}

	public String getLicenseKey() {
		return this.licenseKey;
	}

	public void setLicenseKey(String licenseKey) {
		this.licenseKey = licenseKey;
	}

	public Boolean getOracleSqlplus() {
		return this.oracleSqlplus;
	}

	public void setOracleSqlplus(Boolean oracleSqlplus) {
		this.oracleSqlplus = oracleSqlplus;
	}

	public Boolean getOracleSqlplusWarn() {
		return this.oracleSqlplusWarn;
	}

	public void setOracleSqlplusWarn(Boolean oracleSqlplusWarn) {
		this.oracleSqlplusWarn = oracleSqlplusWarn;
	}

	public String getOracleWalletLocation() {
		return this.oracleWalletLocation;
	}

	public void setOracleWalletLocation(String oracleWalletLocation) {
		this.oracleWalletLocation = oracleWalletLocation;
	}

	public Boolean getStream() {
		return this.stream;
	}

	public void setStream(Boolean stream) {
		this.stream = stream;
	}

	public String getUndoSqlMigrationPrefix() {
		return this.undoSqlMigrationPrefix;
	}

	public void setUndoSqlMigrationPrefix(String undoSqlMigrationPrefix) {
		this.undoSqlMigrationPrefix = undoSqlMigrationPrefix;
	}

	public String[] getCherryPick() {
		return this.cherryPick;
	}

	public void setCherryPick(String[] cherryPick) {
		this.cherryPick = cherryPick;
	}

	public Map<String, String> getJdbcProperties() {
		return this.jdbcProperties;
	}

	public void setJdbcProperties(Map<String, String> jdbcProperties) {
		this.jdbcProperties = jdbcProperties;
	}

	public String getKerberosConfigFile() {
		return this.kerberosConfigFile;
	}

	public void setKerberosConfigFile(String kerberosConfigFile) {
		this.kerberosConfigFile = kerberosConfigFile;
	}

	public String getOracleKerberosCacheFile() {
		return this.oracleKerberosCacheFile;
	}

	public void setOracleKerberosCacheFile(String oracleKerberosCacheFile) {
		this.oracleKerberosCacheFile = oracleKerberosCacheFile;
	}

	@DeprecatedConfigurationProperty(replacement = "spring.flyway.kerberos-config-file")
	@Deprecated
	public String getOracleKerberosConfigFile() {
		return getKerberosConfigFile();
	}

	@Deprecated
	public void setOracleKerberosConfigFile(String oracleKerberosConfigFile) {
		setKerberosConfigFile(oracleKerberosConfigFile);
	}

	public Boolean getOutputQueryResults() {
		return this.outputQueryResults;
	}

	public void setOutputQueryResults(Boolean outputQueryResults) {
		this.outputQueryResults = outputQueryResults;
	}

	public String getSqlServerKerberosLoginFile() {
		return this.sqlServerKerberosLoginFile;
	}

	public void setSqlServerKerberosLoginFile(String sqlServerKerberosLoginFile) {
		this.sqlServerKerberosLoginFile = sqlServerKerberosLoginFile;
	}

	public Boolean getSkipExecutingMigrations() {
		return this.skipExecutingMigrations;
	}

	public void setSkipExecutingMigrations(Boolean skipExecutingMigrations) {
		this.skipExecutingMigrations = skipExecutingMigrations;
	}

	public List<String> getIgnoreMigrationPatterns() {
		return this.ignoreMigrationPatterns;
	}

	public void setIgnoreMigrationPatterns(List<String> ignoreMigrationPatterns) {
		this.ignoreMigrationPatterns = ignoreMigrationPatterns;
	}

	public Boolean getDetectEncoding() {
		return this.detectEncoding;
	}

	public void setDetectEncoding(final Boolean detectEncoding) {
		this.detectEncoding = detectEncoding;
	}

	public String getBaselineMigrationPrefix() {
		return this.baselineMigrationPrefix;
	}

	public void setBaselineMigrationPrefix(String baselineMigrationPrefix) {
		this.baselineMigrationPrefix = baselineMigrationPrefix;
	}

	public String getScriptPlaceholderPrefix() {
		return this.scriptPlaceholderPrefix;
	}

	public void setScriptPlaceholderPrefix(String scriptPlaceholderPrefix) {
		this.scriptPlaceholderPrefix = scriptPlaceholderPrefix;
	}

	public String getScriptPlaceholderSuffix() {
		return this.scriptPlaceholderSuffix;
	}

	public void setScriptPlaceholderSuffix(String scriptPlaceholderSuffix) {
		this.scriptPlaceholderSuffix = scriptPlaceholderSuffix;
	}

}
